/*
 * Licensed this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 * AUTHOR: Sebastian Ritter
 */
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;

namespace org.boapi.shop {
  /// <summary>
  /// Global Trade Item Number [GTIN] is a 13 digits number - 
  /// or as ISBN 13 with an X as check digit.
  /// Since 2005 EAN International and American UCC merge to GS1 and also
  /// EAN and UPC now named as GTIN.
  /// @see http://www.gs1-germany.de/
  /// <version>2.0.0.0</version> 
  /// </summary>
  /// <version>2.0.0.0</version>
  public class GTIN : org.boapi.core.IChecknumber {
    /// <summary>
    /// Create an GTIN from ISBN-13
    /// </summary>
    /// <param name="isbn"></param>
    /// <returns></returns>
    public static GTIN parse (ISBN13 isbn) {
      GTIN gtin = new GTIN ();
      gtin.setCompleteValue (isbn.getNumber ());
      return gtin;
    }

    /// <summary>
    /// Europgtin Article Number International 
    /// </summary>
    private String number;
    /// <summary>
    /// Set the GTIN
    /// </summary>
    /// <param name="gtin"></param>
    public void setCompleteValue (params string[] gtin) {
      StringBuilder nummer = new StringBuilder ();
      foreach (Char c in gtin[0].ToUpper().ToCharArray ()) {
        if (Char.IsDigit (c) || c == 'X') {
          nummer.Append (c);
        }
        else if (c != '-') {
          throw new ArgumentException ("Illegal character in GTIN13", gtin[0]);
        }
      }
      while (nummer.Length < 13) {
        nummer.Insert (0, '0');
      }
      number = nummer.ToString ();
      this.setChecknumber ('R');
    }
    /// <summary>
    /// The number of GTIN
    /// </summary>
    /// <returns></returns>
    public String getNumber () {
      return this.number;
    }
    /// <summary>
    /// The check digit
    /// </summary>
    private char checknumber = 'R';
    /// <summary>
    /// Set the check digit
    /// </summary>
    /// <param name="checkdigit"></param>
    internal void setChecknumber (char checkdigit) {
      this.checknumber = checkdigit;
    }
    /// <summary>
    /// Get the check digit
    /// </summary>
    /// <returns></returns>
    internal char getChecknumber () {
      if (this.checknumber == 'R') {
        this.isChecknumberCorrect ();
      }
      return this.checknumber;
    }
    /// <summary>
    /// Is the checknumber correct
    /// </summary>
    /// <returns>true if check digit is correct</returns>
    public bool isChecknumberCorrect () {
      int[] factors = new int[] { 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3};
      int[] digits = new int[factors.Length];
      for (int i = 0; i < digits.Length; i++) {
        digits[i] = int.Parse(this.getNumber().Substring (i, 1));
      }
      int sum = 0;
      for (int i = 0; i < digits.Length; i++) {
        sum += digits[i] * factors[i];
      }
      int checknumber = 10-(sum % 10);
      this.setChecknumber (char.Parse ("" + checknumber));
      return this.getChecknumber() == this.getNumber().ToCharArray(12,1)[0];
    }

        /// <summary>
        /// Save the i18n detail informations from GTIN 
        /// </summary> 
        private static Dictionary<String,Dictionary<String,String>> localization;

        /// <summary>
        /// The default value for GTIN information of locale
        /// Note: Default value can change with attribute default='default' in GTIN.xml.
        /// </summary>
        private static String defaultGTINLocale = ""; // if you want to change do it in GTIN.xml!

        /// <summary>
        /// Load the i18n detail informations for setting current ui culture language.
        /// </summary>
        /// <see cref="System.Globalization.CultureInfo.CurrentUICulture.TwoLetterISOLanguageName"/>
        /// <exception cref="System.IO.IOException">if not found or XML error</exception>
        private static void loadGTINLocale()
        {
            GTIN.localization = new Dictionary<String, Dictionary<String, String>>();

            XmlReader reader = null;
            try
            {
                String lang = System.Globalization.CultureInfo.CurrentUICulture.TwoLetterISOLanguageName;

                XmlReaderSettings settings = new XmlReaderSettings();
                settings.ProhibitDtd = false;
                reader = XmlReader.Create("./GTIN.xml", settings);

                while (reader.Read())
                {
                    switch (reader.NodeType)
                    {
                        case XmlNodeType.Element:
                            Console.Write(reader.Name);
                            if ("gtin".Equals(reader.Name))
                            {
                                if (lang.Equals(reader.GetAttribute("lang")))
                                {
                                    Dictionary<String, String> values = new Dictionary<string, string>();
                                    GTIN.localization.Add(lang, values);

                                    String locale = null;
                                    while (reader.Read() && !"gtin".Equals(reader.Name))
                                    {
                                        switch (reader.NodeType)
                                        {
                                            case XmlNodeType.Element :
                                                if ("detail".Equals(reader.Name))
                                                {
                                                    locale = reader.GetAttribute("locale");
                                                    if (null != reader.GetAttribute("default"))
                                                    {
                                                        GTIN.defaultGTINLocale = locale;
                                                    }
                                                }
                                                else if ("startsWith".Equals(reader.Name))
                                                {
                                                    values.Add(reader.ReadString(), locale);
                                                }
                                                break;
                                            case XmlNodeType.EndElement :
                                                if ("detail".Equals(reader.Name))
                                                {
                                                    locale = null;
                                                }
                                                break;
                                        }
                                    }
                                    reader.ReadToFollowing("<"); //ok we have what we want and go to end
                                }
                                else { }
                            }
                            break;
                        default:
                            break;
                    }
                    //Console.WriteLine(reader.Name);
                }
            }
            catch (IOException throwing)
            {
                throw new ApplicationException(throwing.Message, throwing);
            }
            catch (XmlException throwing)
            {
                throw new ApplicationException(throwing.Message, throwing);
            }
            finally
            {
                if (null != reader && ReadState.Closed != reader.ReadState)
                {
                    reader.Close();
                }
            }
        }

        public String getLocation()
        {
            if (GTIN.localization == null)
            {
                GTIN.loadGTINLocale();
            }
            String local = GTIN.defaultGTINLocale;

            String lang = System.Globalization.CultureInfo.CurrentUICulture.TwoLetterISOLanguageName;
            Dictionary<String,String> locales = new Dictionary<string,string>();
            GTIN.localization.TryGetValue(lang, out locales);

            String _locale = null;
            
            String startsWith = this.getNumber().Substring(0, 4);
            if (locales.TryGetValue (startsWith, out _locale)) {
    	        local = _locale;
            }
            else {
              startsWith = this.getNumber().Substring(0, 3);
              if (locales.TryGetValue (startsWith, out _locale)) {
    	        local = _locale;
              }
              else {
    	        startsWith = this.getNumber ().Substring(0, 2);
                if (locales.TryGetValue (startsWith, out _locale)) {
    	            local = _locale;
                }
                else {
        	        startsWith = this.getNumber ().Substring(0, 1);
                    if (locales.TryGetValue (startsWith, out _locale)) {
               	        local = _locale;
                    }
                    else {} // local has the default value
                }
            }
            return local;
        }

    /// <summary>
    /// If GTIN starts with 45 or 49 it is also called Japanese Article Number
    /// </summary>
    /// <returns></returns>
    public bool isJAN () {
      bool ok = false;
      if (this.getNumber ().StartsWith ("45") ||
          this.getNumber ().StartsWith ("49")) {
        ok = true;
      }
      return ok;
    }
  }
}
